package barinadroid.game;

import java.util.ArrayList;
import java.util.Random;
import org.andengine.engine.camera.Camera;
import org.andengine.engine.camera.hud.controls.BaseOnScreenControl;
import org.andengine.engine.camera.hud.controls.BaseOnScreenControl.IOnScreenControlListener;
import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.Entity;
import org.andengine.entity.IEntity;
import org.andengine.entity.IEntityFactory;
import org.andengine.entity.particle.ParticleSystem;
import org.andengine.entity.particle.emitter.PointParticleEmitter;
import org.andengine.entity.particle.initializer.VelocityParticleInitializer;
import org.andengine.entity.particle.modifier.AlphaParticleModifier;
import org.andengine.entity.particle.modifier.RotationParticleModifier;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.IOnAreaTouchListener;
import org.andengine.entity.scene.ITouchArea;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.sprite.ButtonSprite;
import org.andengine.entity.sprite.ButtonSprite.OnClickListener;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.input.touch.TouchEvent;
import org.andengine.ui.activity.BaseGameActivity;
import org.andengine.util.color.Color;
import android.util.Log;
import barinadroid.game.GameEntity.EntityIdentity;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactFilter;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;

public class GameScene extends Scene implements OnClickListener, IOnScreenControlListener, ContactFilter, ContactListener
{
	public static GameScene instance;

	public static GameScene getSharedInstance()
	{
		if(instance == null)
			instance = new GameScene();
		return instance;
	}

	Camera mCamera;
	private Ship ship;
	private Rectangle safeZone;
	// private Rectangle topWall, leftWall, bottomWall, rightWall;// ,
	// innerWall;
	private Wall topWall, leftWall, bottomWall, rightWall;
	private PhysicsWorld physicsWorld;
	private ArrayList<Bullet> deadBullets;
	private ArrayList<Enemy> deadEnemies;

	private GameScene()
	{
		setBackground(new Background(new Color(.2f, .2f, .2f)));
		mCamera = GameActivity.getSharedInstance().camera;
		this.physicsWorld = new PhysicsWorld(new Vector2(0, 0), false);
		this.safeZone = new Rectangle(300, 50, GameActivity.getSharedInstance().CAMERA_WIDTH - 600, GameActivity.getSharedInstance().CAMERA_HEIGHT - 100, GameActivity
				.getSharedInstance().getVertexBufferObjectManager());
		this.ship = Ship.getSharedInstance();
		this.ship.setBody(physicsWorld, EntityIdentity.PLAYER);
		createWalls();
		resetValues();
		physicsWorld.setContactListener(this);
		physicsWorld.setContactFilter(this);
		registerUpdateHandler(physicsWorld);
		registerUpdateHandler(new IUpdateHandler()
		{
			@Override
			public void reset()
			{}

			@Override
			public void onUpdate(float arg0)
			{
				doUpdate(arg0);
			}
		});
		ControlsScene.getSharedInstance().registerOnScreenControlChangeListener(this);
		ControlsScene.getSharedInstance().registerButtonAClickListener(this);
		ControlsScene.getSharedInstance().registerButtonBClickListener(this);
		ControlsScene.getSharedInstance().registerButtonATouchListeners(new IOnAreaTouchListener()
		{
			@Override
			public boolean onAreaTouched(TouchEvent pSceneTouchEvent, ITouchArea pTouchArea, float pTouchAreaLocalX, float pTouchAreaLocalY)
			{
				return false;
			}
		});
		ControlsScene.getSharedInstance().registerButtonBTouchListeners(new IOnAreaTouchListener()
		{
			@Override
			public boolean onAreaTouched(TouchEvent pSceneTouchEvent, ITouchArea pTouchArea, float pTouchAreaLocalX, float pTouchAreaLocalY)
			{
				switch (pSceneTouchEvent.getAction())
				{
					case TouchEvent.ACTION_DOWN:
						ship.startFire();
						break;
					case TouchEvent.ACTION_UP:
						ship.stopFire();
						break;
				}
				return false;
			}
		});
	}

	// The Game loop.
	private final void doUpdate(float pSecondsElapsed)
	{
		ship.normalizeShipsBodyVelocity(pSecondsElapsed);
		Bullet b = ship.Shoot();
		if(b != null)
		{
			if(b.getParent() == null)
				attachChild(b);
			b.setActive(true);
		}
		// destroying dead bullets and enemies.
		for(int i = 0 ; i < deadBullets.size() ; i++)
		{
			Bullet bullet = deadBullets.get(i);
			if(bullet.isActive())
			{
				detachChild(bullet);
				BulletFactory.getSharedInstance().DestroyBullet(bullet);
				createExplosion(bullet.getCenter().x, bullet.getCenter().y, this, GameActivity.getSharedInstance());
			}
		}
		for(int i = 0 ; i < deadEnemies.size() ; i++)
		{
			Enemy enemy = deadEnemies.get(i);
			if(enemy.isActive())
			{
				detachChild(enemy);
				EnemyFactory.getSharedInstance().KillEnemy(enemy);
				createExplosion(enemy.getCenter().x, enemy.getCenter().y, this, GameActivity.getSharedInstance());
			}
		}
		deadBullets.clear();
		deadEnemies.clear();
	}

	private void createWalls()
	{
		// final VertexBufferObjectManager vbo =
		// GameActivity.getSharedInstance().getVertexBufferObjectManager();
		final int camWidth = GameActivity.getSharedInstance().CAMERA_WIDTH, camHeight = GameActivity.getSharedInstance().CAMERA_HEIGHT;
		// final Rectangle safeZone = new Rectangle(this.safeZone.getX() - 4,
		// this.safeZone.getY() - 4, this.safeZone.getWidth() + 8,
		// this.safeZone.getHeight() + 8, vbo);
		// topWall = new Rectangle(0, 0, camWidth - safeZone.getX(),
		// safeZone.getY(), vbo);
		// leftWall = new Rectangle(0, topWall.getHeight(), safeZone.getX(),
		// camHeight - topWall.getHeight(), vbo);
		// bottomWall = new Rectangle(leftWall.getWidth(), leftWall.getHeight(),
		// camWidth - leftWall.getWidth(), topWall.getHeight(), vbo);
		// rightWall = new Rectangle(leftWall.getWidth() + safeZone.getWidth(),
		// 0, leftWall.getWidth(), leftWall.getHeight(), vbo);
		// // innerWall = new Rectangle(safeZone.getX() +
		// // ship.getSprite().getWidth() + 4, safeZone.getY() +
		// // ship.getSprite().getHeight() + 4, safeZone.getWidth()
		// // - ship.getSprite().getWidth() * 2f - 8, safeZone.getHeight() -
		// // ship.getSprite().getHeight() * 2f - 8, vbo);
		// Color c = Color.TRANSPARENT;
		// topWall.setColor(c);
		// rightWall.setColor(c);
		// bottomWall.setColor(c);
		// leftWall.setColor(c);
		// // innerWall.setColor(c);
		// PhysicsFactory.createBoxBody(getPhysicsWorld(), topWall,
		// BodyType.StaticBody,
		// EntityIdentity.GENERAL.getFixtureDef()).setUserData(topWall);
		// PhysicsFactory.createBoxBody(getPhysicsWorld(), leftWall,
		// BodyType.StaticBody,
		// EntityIdentity.GENERAL.getFixtureDef()).setUserData(leftWall);
		// PhysicsFactory.createBoxBody(getPhysicsWorld(), bottomWall,
		// BodyType.StaticBody,
		// EntityIdentity.GENERAL.getFixtureDef()).setUserData(bottomWall);
		// PhysicsFactory.createBoxBody(getPhysicsWorld(), rightWall,
		// BodyType.StaticBody,
		// EntityIdentity.GENERAL.getFixtureDef()).setUserData(rightWall);
		// // PhysicsFactory.createBoxBody(physicsWorld, innerWall,
		// // BodyType.StaticBody, WALL_FIX);
		topWall = new Wall(new Vector2(0, 0), new Vector2(camWidth - safeZone.getX(), safeZone.getY()), getPhysicsWorld());
		leftWall = new Wall(new Vector2(0, topWall.getSprite().getHeight()), new Vector2(safeZone.getX(), camHeight - topWall.getSprite().getHeight()), getPhysicsWorld());
		bottomWall = new Wall(new Vector2(leftWall.getSprite().getWidth(), leftWall.getSprite().getHeight()), new Vector2(camWidth - leftWall.getSprite().getWidth(), topWall
				.getSprite().getHeight()), getPhysicsWorld());
		rightWall = new Wall(new Vector2(leftWall.getSprite().getWidth() + safeZone.getWidth(), 0), new Vector2(leftWall.getSprite().getWidth(), leftWall.getSprite().getHeight()),
				getPhysicsWorld());
	}

	public Rectangle getSafeZone()
	{
		// return new
		// Rectangle(leftWall.getSprite().getWidth(),topWall.getSprite().getHeight(),rightWall.getX()-leftWall.getSprite().getWidth(),bottomWall.getY()-topWall.getSprite().getHeight(),GameActivity.getSharedInstance().getVertexBufferObjectManager());
		return this.safeZone;
	}

	@Override
	public void onClick(ButtonSprite pButtonSprite, float pTouchAreaLocalX, float pTouchAreaLocalY)
	{
		if(pButtonSprite == ControlsScene.getSharedInstance().buttonA)
		{}
		else
			if(pButtonSprite == ControlsScene.getSharedInstance().buttonB)
			{
				// gameloop handle ship fire now..
			}
	}

	@Override
	public void onControlChange(BaseOnScreenControl pBaseOnScreenControl, float pValueX, float pValueY)
	{
		ship.setVelocity(pValueX, pValueY);
	}

	public void detach()
	{
		clearUpdateHandlers();
		detachChildren();
		Ship.instance = null;
	}

	// method to reset values and restart the game
	public void resetValues()
	{
		if(deadBullets == null)
			deadBullets = new ArrayList<Bullet>();
		else
			deadBullets.clear();
		if(deadEnemies == null)
			deadEnemies = new ArrayList<Enemy>();
		else
			deadEnemies.clear();
		clearChildScene();
		clearEntityModifiers();
		BulletFactory.getSharedInstance().DestroyAllBullets();
		EnemyFactory.getSharedInstance().KillAllEnemies();
		attachChild(topWall.getSprite());
		attachChild(leftWall.getSprite());
		attachChild(bottomWall.getSprite());
		attachChild(rightWall.getSprite());
		// attachChild(innerWall);
		ship.restart();
		attachChild(ship.getSprite());
		physicsWorld.registerPhysicsConnector(new PhysicsConnector(ship.getSprite(), ship.getBody(), true, true));
//		registerEntityModifier(new LoopEntityModifier(new DelayModifier(2, new IEntityModifierListener()
//		{// Enemy generator 
//			Random rnd = new Random();
//
//			@Override
//			public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem)
//			{}
//
//			@Override
//			public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem)
//			{
//				if(EnemyFactory.getSharedInstance().getEnemies().size() >= 20)
//					return;
//				// float x = leftLine.getX1() * rnd.nextFloat() +
//				// innerWall.getX(), y = innerWall.getHeight() * rnd.nextFloat()
//				// + innerWall.getY();
//				final Enemy enemy = EnemyFactory.getSharedInstance().Spawn(safeZone.getX() + safeZone.getWidth() * rnd.nextFloat(),
//						safeZone.getY() + safeZone.getHeight() * rnd.nextFloat());
//				if(EnemyFactory.getSharedInstance().getEnemies().size() % 2 != 0)
//					enemy.setBehavior(MoveBackAndForthBehavior.getNewBehavior(enemy));
//				else
//					enemy.setBehavior(ChaseAfterEntityBehavior.getNewBehavior(enemy, ship));
//				attachChild(enemy);
//				enemy.setActive(true);
//			}
//		})));
	}

	public PhysicsWorld getPhysicsWorld()
	{
		return physicsWorld;
	}

	// public Rectangle getHotZone()
	// {
	// return this.innerWall;
	// }
	public static void createExplosion(final float posX, final float posY, final IEntity target, final BaseGameActivity activity)
	{
		int mNumPart = 7;
		float mTimePart = 0.5f;
		PointParticleEmitter particleEmitter = new PointParticleEmitter(posX, posY);
		IEntityFactory<Entity> recFact = new IEntityFactory<Entity>()
		{
			@Override
			public Rectangle create(float pX, float pY)
			{
				Rectangle rect = new Rectangle(posX, posY, 15, 7, activity.getVertexBufferObjectManager());
				rect.setColor(Color.YELLOW);
				return rect;
			}
		};
		final ParticleSystem<Entity> particleSystem = new ParticleSystem<Entity>(recFact, particleEmitter, 500, 1500, mNumPart);
		particleSystem.addParticleInitializer(new VelocityParticleInitializer<Entity>(-500, 500, -500, 500));
		particleSystem.addParticleModifier(new AlphaParticleModifier<Entity>(0, 0.6f * mTimePart, 1, 0));
		particleSystem.addParticleModifier(new RotationParticleModifier<Entity>(0, mTimePart, 0, 360));
		target.attachChild(particleSystem);
		target.registerUpdateHandler(new TimerHandler(mTimePart, new ITimerCallback()
		{
			@Override
			public void onTimePassed(final TimerHandler pTimerHandler)
			{
				particleSystem.detachSelf();
				target.sortChildren();
				target.unregisterUpdateHandler(pTimerHandler);
			}
		}));
	}

	@Override
	public boolean shouldCollide(Fixture fixtureA, Fixture fixtureB)
	{
		boolean result = false;
		try
		{
			EntityIdentity idA = ((GameEntity)fixtureA.getBody().getUserData()).getIdentity();
			EntityIdentity idB = ((GameEntity)fixtureB.getBody().getUserData()).getIdentity();
			switch (idA)
			{
				case STATIC:
					break;
				case GENERAL:
					result = (idB == EntityIdentity.PLAYER || idB == EntityIdentity.BULLET || idB == EntityIdentity.ENEMY || idB == EntityIdentity.ENEMY_BULLET || idB == EntityIdentity.GENERAL);
					break;
				case PLAYER:
					result = (idB == EntityIdentity.ENEMY || idB == EntityIdentity.ENEMY_BULLET || idB == EntityIdentity.GENERAL) && idB != EntityIdentity.BULLET;
					break;
				case BULLET:
					result = (idB == EntityIdentity.ENEMY || idB == EntityIdentity.ENEMY_BULLET || idB == EntityIdentity.GENERAL) && idB != EntityIdentity.PLAYER;
					break;
				case ENEMY:
					result = (idB == EntityIdentity.ENEMY || idB == EntityIdentity.PLAYER || idB == EntityIdentity.BULLET || idB == EntityIdentity.GENERAL);
					break;
				case ENEMY_BULLET:
					result = (idB == EntityIdentity.PLAYER || idB == EntityIdentity.BULLET || idB == EntityIdentity.GENERAL);
					break;
			}
			Log.v("GameScene", idA + " should " + (result ? "" : "NOT ") + "colide with " + idB + ".");
		}
		catch(ClassCastException c)
		{
			Log.w("GameScene", "Cast exception: " + c.getMessage());
		}
		return result;
	}

	@Override
	public void beginContact(Contact contact)
	{
		try
		{
			GameEntity entityA = (GameEntity)contact.getFixtureA().getBody().getUserData();
			GameEntity entityB = (GameEntity)contact.getFixtureB().getBody().getUserData();
			EntityIdentity idA = entityA.getIdentity();
			EntityIdentity idB = entityB.getIdentity();
			Log.i("GameScene", idA + " colides with " + idB);
			Ship ship = null;
			Enemy enemy = null;
			Bullet bullet = null;
			Bullet enemyBullet = null;
			Wall wall = null;
			switch (idA)
			{
				case PLAYER:
					ship = (Ship)entityA;
					break;
				case BULLET:
					bullet = (Bullet)entityA;
					if(bullet.getOwner() != this.ship)
					{
						enemyBullet = bullet;
						bullet = null;
					}
					break;
				case ENEMY:
					enemy = (Enemy)entityA;
					break;
				case GENERAL:
					if(entityA instanceof Wall)
						wall = (Wall)entityA;
					break;
				default:
					break;
			}
			switch (idB)
			{
				case PLAYER:
					ship = (Ship)entityB;
					break;
				case BULLET:
					bullet = (Bullet)entityB;
					if(bullet.getOwner() != this.ship)
					{
						enemyBullet = bullet;
						bullet = null;
					}
					break;
				case ENEMY:
					enemy = (Enemy)entityB;
					break;
				case GENERAL:
					if(entityB instanceof Wall)
						wall = (Wall)entityB;
					break;
				default:
					break;
			}
			if(bullet != null)
			{
				if(enemy != null)
				{
					if(enemy.hit(new Random().nextFloat()))
					{
						if(!deadEnemies.contains(enemy))
							deadEnemies.add(enemy);
					}
					if(!deadBullets.contains(bullet))
						deadBullets.add(bullet);
				}
				else
					if(wall != null)
					{
						if(!deadBullets.contains(bullet))
							deadBullets.add(bullet);
					}
			}
			if(enemyBullet != null)
			{
				if(ship != null)
				{
					// TODO: hit players ship..
				}
				else
					if(wall != null)
					{
						if(!deadBullets.contains(bullet))
							deadBullets.add(bullet);
					}
			}
		}
		catch(ClassCastException c)
		{
			Log.w("GameScene", "Exception cause: '" + c.getCause() + "' Exception msg: '" + c.getMessage() + "'");
		}
	}

	@Override
	public void endContact(Contact contact)
	{
		// TODO Auto-generated method stub
	}

	@Override
	public void preSolve(Contact contact, Manifold oldManifold)
	{
		// TODO Auto-generated method stub
	}

	@Override
	public void postSolve(Contact contact, ContactImpulse impulse)
	{
		// TODO Auto-generated method stub
	}
}